home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / motif / textfun.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  25.8 KB  |  865 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /* $Revision: 1.5 $ */
  18. /* compile: cc -o textfun textfun.c -lXm -lXt -lGL -lXext -lX11 -lm */
  19.  
  20. /*
  21.  * textfun demonstrates pulling X bitmap fonts from the X server into an
  22.  * OpenGL client and converting the bitmaps into OpenGL display lists with
  23.  * transformable geometry.  Text can then be displayed from any perspective
  24.  * in 3D.
  25.  * 
  26.  * Motif is used for the user interface.  The program renders OpenGL into a
  27.  * standard Motif drawing area and does not use any special OpenGL widget.
  28.  * Pull down menus with toggles and radio buttons are used.  The animation is
  29.  * controled by X Toolkit work procs; iconfiying textfun will stop the work
  30.  * proc and resume it when the program is uniconified.
  31.  * 
  32.  * Various fonts can be switched between.  A number of the fonts are X 
  33.  * scalable fonts demonstrating how the blocky nature of the text can be
  34.  * minimized with higher resolution fonts.
  35.  *
  36.  * note the Makefile's textfun5.1 compile rule employing the 
  37.  * IRIX_5_1_MOTIF_BUG_WORKAROUND flag for a Motif bug in IRIX 5.1 
  38.  * 
  39.  * Mark J. Kilgard
  40.  * mjk@sgi.com
  41.  * Silicon Graphics, Inc.
  42.  * March 7, 1994
  43.  */
  44.  
  45. #include <stdlib.h>
  46. #include <stdio.h>
  47. #include <unistd.h>
  48. #include <math.h>
  49. #ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
  50. #include <sys/utsname.h>
  51. #endif
  52. #include <Xm/MainW.h>
  53. #include <Xm/RowColumn.h>
  54. #include <Xm/PushB.h>
  55. #include <Xm/ToggleB.h>
  56. #include <Xm/CascadeB.h>
  57. #include <Xm/Frame.h>
  58. #include <Xm/DrawingA.h>
  59. #include <X11/keysym.h>
  60. #include <GL/gl.h>
  61. #include <GL/glu.h>
  62. #include <GL/glx.h>
  63.  
  64. #ifdef DEBUG
  65. #define GL_ERROR_CHECK() \
  66.     { /* for help debugging, report any OpenGL errors that occur per frame */ \
  67.         GLenum error; \
  68.         while((error = glGetError()) != GL_NO_ERROR) \
  69.             fprintf(stderr, "GL error: %s, line %d\n", gluErrorString(error), __LINE__); \
  70.     }
  71. #else
  72. #define GL_ERROR_CHECK() { /* nothing */ }
  73. #endif
  74.  
  75. typedef struct {
  76.     short           width;
  77.     short           height;
  78.     short           xoffset;
  79.     short           yoffset;
  80.     short           advance;
  81.     char           *bitmap;
  82. }               PerCharInfo, *PerCharInfoPtr;
  83.  
  84. typedef struct {
  85.     int             min_char;
  86.     int             max_char;
  87.     int             max_ascent;
  88.     int             max_descent;
  89.     GLuint          dlist_base;
  90.     PerCharInfo     glyph[1];
  91. }               FontInfo, *FontInfoPtr;
  92.  
  93. typedef struct {
  94.     char           *name;
  95.     char           *xlfd;
  96.     XFontStruct    *xfont;
  97.     FontInfoPtr     fontinfo;
  98. }               FontEntry, *FontEntryPtr;
  99.  
  100. static int      dblBuf[] =
  101. {
  102.     GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16,
  103.     GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
  104.     None
  105. };
  106. static int     *snglBuf = &dblBuf[1];
  107.  
  108. static String   fallbackResources[] =
  109. {
  110.     "*sgiMode: true",        /* try to enable IRIX 5.2+ look & feel */
  111.     "*useSchemes: all",        /* and SGI schemes */
  112.     "*title: OpenGL text transformation",
  113.     "*glxarea*width: 300", "*glxarea*height: 300", NULL
  114. };
  115.  
  116. static FontEntry fontEntry[] =
  117. {
  118.     {"Fixed", "fixed", NULL, NULL},
  119.     {"Utopia", "-adobe-utopia-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  120.     {"Schoolbook", "-adobe-new century schoolbook-bold-i-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  121.     {"Rock", "-sgi-rock-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  122.     {"Rock (hi-res)", "-sgi-rock-medium-r-normal--50-*-*-*-p-*-iso8859-1", NULL, NULL},
  123.     {"Curl", "-sgi-curl-medium-r-normal--20-*-*-*-p-*-*-*", NULL, NULL},
  124.     {"Curl (hi-res)", "-sgi-curl-medium-r-normal--50-*-*-*-p-*-*-*", NULL, NULL},
  125.     {"Dingbats", "-adobe-itc zapf dingbats-medium-r-normal--35-*-*-*-p-*-adobe-fontspecific", NULL, NULL}
  126. };
  127. #define NUM_FONT_ENTRIES sizeof(fontEntry)/sizeof(FontEntry)
  128.  
  129. static char    *defaultMessage[] =
  130. {"OpenGL ", "rocks into", "the Future!"};
  131. #define NUM_DEFAULT_MESSAGES sizeof(defaultMessage)/sizeof(char*)
  132.  
  133. Display        *dpy;
  134. GLboolean       doubleBuffer = GL_TRUE, motion = GL_FALSE, rotation = GL_FALSE,
  135.                 wobbling = GL_FALSE, made_current = GL_FALSE, dollying = GL_TRUE;
  136. XtAppContext    app;
  137. XtWorkProcId    workId = 0;
  138. Widget          toplevel = NULL;
  139. Widget        mainw, menubar, menupane, btn, cascade, frame, glxarea;
  140. GLXContext      cx;
  141. XVisualInfo    *vi;
  142. Colormap        cmap;
  143. Arg             menuPaneArgs[4], args[1];
  144. GLfloat         theta = 0, delta = 0;
  145. GLfloat         distance = 19, angle = 0, wobble_angle = 0;
  146. GLuint          base;
  147. int             numMessages;
  148. char          **messages;
  149.  
  150. void 
  151. draw(Widget w)
  152. {
  153.     GLfloat         red, green, blue;
  154.     int             i;
  155.  
  156.     glClear(GL_DEPTH_BUFFER_BIT);
  157.  
  158.     /* paint black to blue smooth shaded polygon for background */
  159.     glDisable(GL_DEPTH_TEST);
  160.     glShadeModel(GL_SMOOTH);
  161.     glBegin(GL_POLYGON);
  162.     glColor3f(1.0, 1.0, 1.0);
  163.     glVertex3f(-20, 20, -19);
  164.     glVertex3f(20, 20, -19);
  165.     glColor3f(0.0, 0.0, 1.0);
  166.     glVertex3f(20, -20, -19);
  167.     glVertex3f(-20, -20, -19);
  168.     glEnd();
  169.  
  170.     glEnable(GL_DEPTH_TEST);
  171.     glShadeModel(GL_FLAT);
  172.  
  173.     glPushMatrix();
  174.     glTranslatef(0, 0, -distance);
  175.     glRotatef(angle, 0, 0, 1);
  176.     glRotatef(wobble_angle, 0, 1, 0);
  177.     glCallList(base);
  178.     glPopMatrix();
  179.  
  180.     if (doubleBuffer)
  181.     glXSwapBuffers(dpy, XtWindow(w));
  182.     if (!glXIsDirect(dpy, cx))
  183.     glFinish();        /* avoid indirect rendering latency from
  184.                  * queuing */
  185.     GL_ERROR_CHECK();
  186. }
  187.  
  188. void 
  189. resize(Widget w, XtPointer data, XtPointer callData)
  190. {
  191.     Dimension       width, height;
  192.  
  193.     /*
  194.      * It is possible for a drawing area widget's resize callback to be
  195.      * called before the window is realized, and therefore before we have
  196.      * made our OpenGL context to the window ID.  So only let the glViewPort
  197.      * call happen if we really have "made_current".
  198.      */
  199.     if (made_current) {
  200.     XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
  201.     glViewport(0, 0, (GLint) width, (GLint) height);
  202.     }
  203. }
  204.  
  205. void 
  206. tick(void)
  207. {
  208.     if (dollying) {
  209.     theta += 0.1;
  210.     distance = cos(theta) * 7 + 12;
  211.     }
  212.     if (rotation)
  213.     angle -= 6;
  214.     if (wobbling) {
  215.     delta += 0.1;
  216.     wobble_angle = sin(delta) * 40;
  217.     }
  218. }
  219.  
  220. Boolean 
  221. animate(XtPointer data)
  222. {
  223.     tick();
  224.     draw(glxarea);
  225.     return False;        /* leave work proc active */
  226. }
  227.  
  228. void 
  229. syncstate(void)
  230. {
  231.     if (motion && (dollying || rotation || wobbling)) {
  232.     if (workId == 0)
  233.         workId = XtAppAddWorkProc(app, animate, NULL);
  234.     } else if (workId != 0) {
  235.     XtRemoveWorkProc(workId);
  236.     workId = 0;
  237.     }
  238. }
  239.  
  240. void 
  241. toggle(void)
  242. {
  243.     motion = !motion;        /* toggle */
  244.     syncstate();
  245. }
  246.  
  247. void 
  248. dolly(void)
  249. {
  250.     dollying = !dollying;    /* toggle */
  251.     syncstate();
  252. }
  253.  
  254. void 
  255. rotate(void)
  256. {
  257.     rotation = !rotation;    /* toggle */
  258.     syncstate();
  259. }
  260.  
  261. void 
  262. wobble(void)
  263. {
  264.     wobbling = !wobbling;    /* toggle */
  265.     syncstate();
  266. }
  267.  
  268. void 
  269. quit(Widget w, XtPointer data, XtPointer callData)
  270. {
  271.     exit(0);
  272. }
  273.  
  274. void 
  275. input(Widget w, XtPointer data, XtPointer callData)
  276. {
  277.     XmDrawingAreaCallbackStruct *cd = (XmDrawingAreaCallbackStruct *) callData;
  278.     char            buf[1];
  279.     KeySym          keysym;
  280.     int             rc;
  281.  
  282.     if (cd->event->type == KeyPress)
  283.     if (XLookupString((XKeyEvent *) cd->event, buf, 1, &keysym, NULL) == 1)
  284.         switch (keysym) {
  285.         case XK_space:
  286.         if (!motion) {    /* advance one frame if not in motion */
  287.             tick();
  288.             draw(w);
  289.         }
  290.         break;
  291.         case XK_Escape:
  292.         exit(0);
  293.         }
  294. }
  295.  
  296. void 
  297. map_state_changed(Widget w, XtPointer data, XEvent * event, Boolean * cont)
  298. {
  299.     switch (event->type) {
  300.     case MapNotify:
  301.     syncstate();
  302.     break;
  303.     case UnmapNotify:
  304.     if (motion) {
  305.         XtRemoveWorkProc(workId);
  306.         workId = 0;
  307.         }
  308.     break;
  309.     }
  310. }
  311.  
  312. /* #define REPORT_GLYPHS */
  313. #ifdef REPORT_GLYPHS
  314. #define DEBUG_GLYPH4(msg,a,b,c,d) printf(msg,a,b,c,d)
  315. #define DEBUG_GLYPH(msg) printf(msg)
  316. #else
  317. #define DEBUG_GLYPH4(msg,a,b,c,d) { /* nothing */ }
  318. #define DEBUG_GLYPH(msg) { /* nothing */ }
  319. #endif
  320.  
  321. #define MAX_GLYPHS_PER_GRAB 512 /* this is big enough for 2^9 glyph character sets */
  322.  
  323. FontInfoPtr
  324. SuckGlyphsFromServer(Display * dpy, Font font)
  325. {
  326.     Pixmap          offscreen;
  327.     XFontStruct    *fontinfo;
  328.     XImage         *image;
  329.     GC              xgc;
  330.     XGCValues       values;
  331.     int             numchars;
  332.     int             width, height, pixwidth;
  333.     int             i, j;
  334.     XCharStruct    *charinfo;
  335.     XChar2b         character;
  336.     char           *bitmapData;
  337.     int             x, y;
  338.     int             spanLength;
  339.     int             charWidth, charHeight, maxSpanLength;
  340.     int             grabList[MAX_GLYPHS_PER_GRAB];
  341.     int             glyphsPerGrab = MAX_GLYPHS_PER_GRAB;
  342.     int             numToGrab, thisglyph;
  343.     FontInfoPtr     myfontinfo;
  344.  
  345.     fontinfo = XQueryFont(dpy, font);
  346.     if (!fontinfo)
  347.     return NULL;
  348.  
  349.     numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1;
  350.     if (numchars < 1)
  351.     return NULL;
  352.  
  353.     myfontinfo = (FontInfoPtr) malloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerCharInfo));
  354.     if (!myfontinfo)
  355.     return NULL;
  356.  
  357.     myfontinfo->min_char = fontinfo->min_char_or_byte2;
  358.     myfontinfo->max_char = fontinfo->max_char_or_byte2;
  359.     myfontinfo->max_ascent = fontinfo->max_bounds.ascent;
  360.     myfontinfo->max_descent = fontinfo->max_bounds.descent;
  361.     myfontinfo->dlist_base = 0;
  362.  
  363.     width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing;
  364.     height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent;
  365.  
  366.     maxSpanLength = (width + 7) / 8;
  367.     /*
  368.      * Be careful determining the width of the pixmap; the X protocol allows
  369.      * pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates
  370.      * max out at 2^15-1 (signed short size).  If the width is too large,
  371.      * we need to limit the glyphs per grab.
  372.      */
  373.     if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) {
  374.     glyphsPerGrab = (1 << 15) / (8 * maxSpanLength);
  375.     }
  376.     pixwidth = glyphsPerGrab * 8 * maxSpanLength;
  377.     offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)),
  378.                   pixwidth, height, 1);
  379.  
  380.     values.font = font;
  381.     values.background = 0;
  382.     values.foreground = 0;
  383.     xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values);
  384.  
  385.     XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  386.     XSetForeground(dpy, xgc, 1);
  387.  
  388.     numToGrab = 0;
  389.     if (fontinfo->per_char == NULL) {
  390.     charinfo = &(fontinfo->min_bounds);
  391.     charWidth = charinfo->rbearing - charinfo->lbearing;
  392.     charHeight = charinfo->ascent + charinfo->descent;
  393.     spanLength = (charWidth + 7) / 8;
  394.     }
  395.     for (i = 0; i < numchars; i++) {
  396.     if (fontinfo->per_char != NULL) {
  397.         charinfo = &(fontinfo->per_char[i]);
  398.         charWidth = charinfo->rbearing - charinfo->lbearing;
  399.         charHeight = charinfo->ascent + charinfo->descent;
  400.         if (charWidth == 0 || charHeight == 0) {
  401.         /* Still must move raster pos even if empty character */
  402.         myfontinfo->glyph[i].width = 0;
  403.         myfontinfo->glyph[i].height = 0;
  404.         myfontinfo->glyph[i].xoffset = 0;
  405.         myfontinfo->glyph[i].yoffset = 0;
  406.         myfontinfo->glyph[i].advance = charinfo->width;
  407.         myfontinfo->glyph[i].bitmap = NULL;
  408.         goto PossiblyDoGrab;
  409.         }
  410.     }
  411.     grabList[numToGrab] = i;
  412.  
  413.     /* XXX is this right for large fonts? */
  414.     character.byte2 = (i + fontinfo->min_char_or_byte2) & 255;
  415.     character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8;
  416.  
  417.     /*
  418.      * XXX we could use XDrawImageString16 which would also paint the
  419.      * backing rectangle but X server bugs in some scalable font
  420.      * rasterizers makes it more effective to do XFillRectangles to clear
  421.      * the pixmap and XDrawImage16 for the text.
  422.      */
  423.     XDrawString16(dpy, offscreen, xgc,
  424.               -charinfo->lbearing + 8 * maxSpanLength * numToGrab,
  425.               charinfo->ascent, &character, 1);
  426.  
  427.     numToGrab++;
  428.  
  429.       PossiblyDoGrab:
  430.  
  431.     if (numToGrab >= glyphsPerGrab || i == numchars - 1) {
  432.         image = XGetImage(dpy, offscreen,
  433.           0, 0, pixwidth, height, 1, XYPixmap);
  434.         for (j = 0; j < numToGrab; j++) {
  435.         thisglyph = grabList[j];
  436.         if (fontinfo->per_char != NULL) {
  437.             charinfo = &(fontinfo->per_char[thisglyph]);
  438.             charWidth = charinfo->rbearing - charinfo->lbearing;
  439.             charHeight = charinfo->ascent + charinfo->descent;
  440.             spanLength = (charWidth + 7) / 8;
  441.         }
  442.         bitmapData = calloc(height * spanLength, sizeof(char));
  443.         if (!bitmapData)
  444.             goto FreeFontAndReturn;
  445.                 DEBUG_GLYPH4("index %d, glyph %d (%d by %d)\n",
  446.             j, thisglyph + fontinfo->min_char_or_byte2, charWidth, charHeight);
  447.         for (y = 0; y < charHeight; y++) {
  448.             for (x = 0; x < charWidth; x++) {
  449.             /*
  450.              * XXX The algorithm used to suck across the font ensures
  451.              * that each glyph begins on a byte boundary.  In theory
  452.              * this would make it convienent to copy the glyph into
  453.              * a byte oriented bitmap.  We actually use the XGetPixel
  454.              * function to extract each pixel from the image which is
  455.              * not that efficient.  We could either do tighter packing
  456.              * in the pixmap or more efficient extraction from the
  457.              * image.  Oh well.
  458.              */
  459.             if (XGetPixel(image, j * maxSpanLength * 8 + x, charHeight - 1 - y)) {
  460.                 DEBUG_GLYPH("x");
  461.                 bitmapData[y * spanLength + x / 8] |= (1 << (x & 7));
  462.             } else {
  463.                 DEBUG_GLYPH(" ");
  464.             }
  465.             }
  466.             DEBUG_GLYPH("\n");
  467.         }
  468.         myfontinfo->glyph[thisglyph].width = charWidth;
  469.         myfontinfo->glyph[thisglyph].height = charHeight;
  470.         myfontinfo->glyph[thisglyph].xoffset = -charinfo->lbearing;
  471.         myfontinfo->glyph[thisglyph].yoffset = charinfo->descent;
  472.         myfontinfo->glyph[thisglyph].advance = charinfo->width;
  473.         myfontinfo->glyph[thisglyph].bitmap = bitmapData;
  474.         }
  475.         XDestroyImage(image);
  476.         numToGrab = 0;
  477.         /* do we need to clear the offscreen pixmap to get more? */
  478.         if (i < numchars - 1) {
  479.         XSetForeground(dpy, xgc, 0);
  480.         XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  481.         XSetForeground(dpy, xgc, 1);
  482.         }
  483.     }
  484.     }
  485.     XFreeGC(dpy, xgc);
  486.     XFreePixmap(dpy, offscreen);
  487.     return myfontinfo;
  488.  
  489.   FreeFontAndReturn:
  490.     XDestroyImage(image);
  491.     XFreeGC(dpy, xgc);
  492.     XFreePixmap(dpy, offscreen);
  493.     for (j = i - 1; j >= 0; j--) {
  494.     if (myfontinfo->glyph[j].bitmap)
  495.         free(myfontinfo->glyph[j].bitmap);
  496.     }
  497.     free(myfontinfo);
  498.     return NULL;
  499. }
  500.  
  501. void
  502. MakeCube(void)
  503. {
  504.     /*
  505.      * No back side to the cube is drawn since the animation makes sure the
  506.      * back side can never be visible.  The "wobble" function is constrained
  507.      * so not to rotate far enough around to reveal the back side.
  508.      */
  509.     glNewList(1, GL_COMPILE);
  510.     glBegin(GL_QUAD_STRIP);
  511.     /* back left post */
  512.     glColor3f(6.0, 0.5, 0.5);
  513.     glVertex3f(0, 0, 0);
  514.     glVertex3f(0, 1, 0);
  515.     /* front left post */
  516.     glVertex3f(0, 0, 1);
  517.     glVertex3f(0, 1, 1);
  518.     glColor3f(1.0, 0.0, 0.0);
  519.     /* front right post */
  520.     glVertex3f(1, 0, 1);
  521.     glVertex3f(1, 1, 1);
  522.     /* back right post */
  523.     glColor3f(6.0, 0.5, 0.5);
  524.     glVertex3f(1, 0, 0);
  525.     glVertex3f(1, 1, 0);
  526.     glEnd();
  527.     glBegin(GL_QUADS);
  528.     /* top face */
  529.     glVertex3f(1, 1, 0);
  530.     glVertex3f(1, 1, 1);
  531.     glVertex3f(0, 1, 1);
  532.     glVertex3f(0, 1, 0);
  533.     /* bottom face */
  534.     glVertex3f(1, 0, 0);
  535.     glVertex3f(1, 0, 1);
  536.     glVertex3f(0, 0, 1);
  537.     glVertex3f(0, 0, 0);
  538.     glEnd();
  539.     glEndList();
  540. }
  541.  
  542. void
  543. MakeGlyphDisplayList(FontInfoPtr font, int c)
  544. {
  545.     PerCharInfoPtr  glyph;
  546.     char           *bitmapData;
  547.     int             width, height, spanLength;
  548.     int             x, y;
  549.  
  550.     if (c < font->min_char || c > font->max_char)
  551.     return;
  552.     if (font->dlist_base == 0) {
  553.     font->dlist_base = glGenLists(font->max_char - font->min_char + 1);
  554.     if (font->dlist_base == 0)
  555.         XtAppError(app, "could not generate font display lists");
  556.     }
  557.     glyph = &font->glyph[c - font->min_char];
  558.     glNewList(c - font->min_char + font->dlist_base, GL_COMPILE);
  559.     bitmapData = glyph->bitmap;
  560.     if (bitmapData) {
  561.     int             oldx = 0, oldy = 0;
  562.  
  563.     glPushMatrix();
  564.     glTranslatef(-glyph->xoffset, -glyph->yoffset, 0);
  565.     width = glyph->width;
  566.     spanLength = (width + 7) / 8;
  567.     height = glyph->height;
  568.     for (x = 0; x < width; x++) {
  569.         for (y = 0; y < height; y++) {
  570.         if (bitmapData[y * spanLength + x / 8] & (1 << (x & 7))) {
  571.             int             y1, count;
  572.  
  573.                     /*
  574.              * Fonts tend to have good vertical repetion.  If we find that
  575.              * the vertically adjacent  pixels in the glyph bitmap are also enabled,
  576.              * we can scale a single cube instead of drawing a cube per pixel.
  577.              */
  578.             for (y1 = y + 1, count = 1; y < height; y1++, count++) {
  579.             if (!(bitmapData[y1 * spanLength + x / 8] & (1 << (x & 7))))
  580.                 break;
  581.             }
  582.             glTranslatef(x - oldx, y - oldy, 0);
  583.             oldx = x;
  584.             oldy = y;
  585.             if (count > 1) {
  586.             glPushMatrix();
  587.             glScalef(1, count, 1);
  588.             glCallList(1);
  589.             glPopMatrix();
  590.             y += count - 1;
  591.             } else {
  592.             glCallList(1);
  593.             }
  594.         }
  595.         }
  596.     }
  597.     glPopMatrix();
  598.     }
  599.     glTranslatef(glyph->advance, 0, 0);
  600.     glEndList();
  601. }
  602.  
  603. GLuint
  604. GetGlyphDisplayList(FontInfoPtr font, int c)
  605. {
  606.     PerCharInfoPtr  glyph;
  607.  
  608.     if (c < font->min_char || c > font->max_char)
  609.     return 0;
  610.     if (font->dlist_base == 0)
  611.     XtAppError(app, "font not display listed");
  612.     return c - font->min_char + font->dlist_base;
  613. }
  614.  
  615. MakeStringDisplayList(FontInfoPtr font, unsigned char *message, GLuint dlist)
  616. {
  617.     unsigned char  *c;
  618.  
  619.     for (c = message; *c != '\0'; c++) {
  620.     MakeGlyphDisplayList(font, *c);
  621.     }
  622.     glNewList(dlist, GL_COMPILE);
  623.     for (c = message; *c != '\0'; c++) {
  624.     glCallList(GetGlyphDisplayList(font, *c));
  625.     }
  626.     glEndList();
  627. }
  628.  
  629. int
  630. GetStringLength(FontInfoPtr font, unsigned char *message)
  631. {
  632.     unsigned char  *c;
  633.     int             ch;
  634.     int             width = 0;
  635.  
  636.     for (c = message; *c != '\0'; c++) {
  637.     ch = *c;
  638.     if (ch >= font->min_char && ch <= font->max_char) {
  639.         width += font->glyph[ch - font->min_char].advance;
  640.     }
  641.     }
  642.     return width;
  643. }
  644.  
  645. SetupMessageDisplayList(FontEntryPtr fontEntry, int num, char *message[])
  646. {
  647.     FontInfoPtr     fontinfo = fontEntry->fontinfo;
  648.     GLfloat         scaleFactor;
  649.     int             totalHeight, maxWidth, height, width;
  650.     int             i;
  651.  
  652.     if (!fontinfo) {
  653.     fontinfo = SuckGlyphsFromServer(dpy, fontEntry->xfont->fid);
  654.     fontEntry->fontinfo = fontinfo;
  655.     }
  656.     height = fontinfo->max_ascent + fontinfo->max_descent;
  657.     maxWidth = 0;
  658.     for (i = 0; i < num; i++) {
  659.     MakeStringDisplayList(fontinfo, message[i], base + i + 1);
  660.     width = GetStringLength(fontinfo, message[i]);
  661.     if (width > maxWidth)
  662.         maxWidth = width;
  663.     }
  664.  
  665. #define SHRINK_FACTOR 25.0    /* empirical */
  666.  
  667.     totalHeight = height * num - fontinfo->max_descent;
  668.     if (maxWidth > totalHeight) {
  669.     scaleFactor = SHRINK_FACTOR / maxWidth;
  670.     } else {
  671.     scaleFactor = SHRINK_FACTOR / totalHeight;
  672.     }
  673.  
  674.     glNewList(base, GL_COMPILE);
  675.     glScalef(scaleFactor, scaleFactor, 1); /* 1 in Z gives glyphs constant depth */
  676.     for (i = 0; i < num; i++) {
  677.     glPushMatrix();
  678.     width = GetStringLength(fontinfo, message[i]);
  679.     glTranslatef(-width / 2.0, height * (num - i - 1) - totalHeight / 2.0, 0);
  680.     glCallList(base + i + 1);
  681.     glPopMatrix();
  682.     }
  683.     glEndList();
  684. }
  685.  
  686. void
  687. fontSelect(Widget widget, XtPointer client_data, XmRowColumnCallbackStruct * cbs)
  688. {
  689.     XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *) cbs->callbackstruct;
  690.     FontEntryPtr    fontEntry = (FontEntryPtr) cbs->data;
  691.  
  692.     if (state->set) {
  693.     SetupMessageDisplayList(fontEntry, numMessages, messages);
  694.     if (!motion)
  695.         draw(glxarea);
  696.     }
  697. }
  698.  
  699. void
  700. neverCalled(void)
  701. {
  702. }
  703.  
  704. void
  705. main(int argc, char *argv[])
  706. {
  707.     int             i;
  708. #ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
  709.     /*
  710.      * XXX Unfortunately a bug in the IRIX 5.1 Motif shared library
  711.      * causes a BadMatch X protocol error if the SGI look&feel
  712.      * is enabled for this program.  If we detect we are on an
  713.      * IRIX 5.1 system, skip the first two fallback resources which
  714.      * specify using the SGI look&feel.
  715.      */
  716.     struct utsname versionInfo;
  717.  
  718.     if(uname(&versionInfo) >= 0) {
  719.         if(!strcmp(versionInfo.sysname, "IRIX") &&
  720.        !strncmp(versionInfo.release, "5.1", 3)) {
  721.             toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  722.                        &fallbackResources[2], NULL, 0);
  723.         }
  724.     }
  725.     if(toplevel == NULL) {
  726.         toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  727.                        fallbackResources, NULL, 0);
  728.     }
  729. #else
  730.     toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  731.                    fallbackResources, NULL, 0);
  732. #endif
  733.     dpy = XtDisplay(toplevel);
  734.     /* find an OpenGL-capable RGB visual with depth buffer */
  735.     vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
  736.     if (vi == NULL) {
  737.     vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
  738.     if (vi == NULL)
  739.         XtAppError(app, "no RGB visual with depth buffer");
  740.     doubleBuffer = GL_FALSE;
  741.     }
  742.     for (i = 0; i < NUM_FONT_ENTRIES; i++) {
  743.     fontEntry[i].xfont = XLoadQueryFont(dpy, fontEntry[i].xlfd);
  744.     if (i == 0 && !fontEntry[i].xfont)
  745.         XtAppError(app, "could not get basic font");
  746.     }
  747.  
  748.     fontEntry[0].fontinfo = SuckGlyphsFromServer(dpy, fontEntry[0].xfont->fid);
  749.     if (!fontEntry[0].fontinfo)
  750.     XtAppError(app, "could not get font glyphs");
  751.  
  752.     /* create an OpenGL rendering context */
  753.     cx = glXCreateContext(dpy, vi, /* no display list sharing */ None,
  754.                /* favor direct */ GL_TRUE);
  755.     if (cx == NULL)
  756.     XtAppError(app, "could not create rendering context");
  757.  
  758.     /* create an X colormap since probably not using default visual */
  759.     cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
  760.                vi->visual, AllocNone);
  761.     XtVaSetValues(toplevel, XtNvisual, vi->visual, XtNdepth, vi->depth,
  762.           XtNcolormap, cmap, NULL);
  763.     XtAddEventHandler(toplevel, StructureNotifyMask, False,
  764.               map_state_changed, NULL);
  765.     mainw = XmCreateMainWindow(toplevel, "mainw", NULL, 0);
  766.     XtManageChild(mainw);
  767.  
  768.     /* create menu bar */
  769.     menubar = XmCreateMenuBar(mainw, "menubar", NULL, 0);
  770.     XtManageChild(menubar);
  771.     /* hack around Xt's ignorance of visuals */
  772.     XtSetArg(menuPaneArgs[0], XmNdepth, DefaultDepthOfScreen(XtScreen(mainw)));
  773.     XtSetArg(menuPaneArgs[1],
  774.          XmNcolormap, DefaultColormapOfScreen(XtScreen(mainw)));
  775.  
  776.     /* create File pulldown menu: Quit */
  777.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
  778.     btn = XmCreatePushButton(menupane, "Quit", NULL, 0);
  779.     XtAddCallback(btn, XmNactivateCallback, quit, NULL);
  780.     XtManageChild(btn);
  781.     XtSetArg(args[0], XmNsubMenuId, menupane);
  782.     cascade = XmCreateCascadeButton(menubar, "File", args, 1);
  783.     XtManageChild(cascade);
  784.  
  785.     /* create Options pulldown menu: Motion, Dolly, Rotate, Wobble */
  786.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
  787.     btn = XmCreateToggleButton(menupane, "Motion", NULL, 0);
  788.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) toggle, NULL);
  789.     XtManageChild(btn);
  790.     btn = XmCreateToggleButton(menupane, "Dolly", NULL, 0);
  791.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) dolly, NULL);
  792.     XtVaSetValues(btn, XmNset, True, NULL);
  793.     XtManageChild(btn);
  794.     btn = XmCreateToggleButton(menupane, "Rotate", NULL, 0);
  795.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) rotate, NULL);
  796.     XtManageChild(btn);
  797.     btn = XmCreateToggleButton(menupane, "Wobble", NULL, 0);
  798.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) wobble, NULL);
  799.     XtManageChild(btn);
  800.     XtSetArg(args[0], XmNsubMenuId, menupane);
  801.     cascade = XmCreateCascadeButton(menubar, "Options", args, 1);
  802.     XtManageChild(cascade);
  803.  
  804.     XtSetArg(menuPaneArgs[2], XmNradioBehavior, True);
  805.     XtSetArg(menuPaneArgs[3], XmNradioAlwaysOne, True);
  806.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 4);
  807.     XtAddCallback(menupane, XmNentryCallback, (XtCallbackProc) fontSelect, NULL);
  808.     for (i = 0; i < NUM_FONT_ENTRIES; i++) {
  809.     btn = XmCreateToggleButton(menupane, fontEntry[i].name, NULL, 0);
  810.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) neverCalled, &fontEntry[i]);
  811.     if (i == 0)
  812.         XtVaSetValues(btn, XmNset, True, NULL);
  813.         if (!fontEntry[i].xfont)
  814.         XtSetSensitive(btn, False);
  815.     XtManageChild(btn);
  816.     }
  817.     XtSetArg(args[0], XmNsubMenuId, menupane);
  818.     cascade = XmCreateCascadeButton(menubar, "Font", args, 1);
  819.     XtManageChild(cascade);
  820.  
  821.     /* create framed drawing area for OpenGL rendering */
  822.     frame = XmCreateFrame(mainw, "frame", NULL, 0);
  823.     XtManageChild(frame);
  824.     glxarea = XtCreateManagedWidget("glxarea", xmDrawingAreaWidgetClass,
  825.                     frame, NULL, 0);
  826.     XtAddCallback(glxarea, XmNexposeCallback, (XtCallbackProc) draw, NULL);
  827.     XtAddCallback(glxarea, XmNresizeCallback, resize, NULL);
  828.     XtAddCallback(glxarea, XmNinputCallback, input, NULL);
  829.     /* set up application's window layout */
  830.     XmMainWindowSetAreas(mainw, menubar, NULL, NULL, NULL, frame);
  831.     XtRealizeWidget(toplevel);
  832.  
  833.     /*
  834.      * Once widget is realized (ie, associated with a created X window), we
  835.      * can bind the OpenGL rendering context to the window.
  836.      */
  837.     glXMakeCurrent(dpy, XtWindow(glxarea), cx);
  838.     made_current = GL_TRUE;
  839.     /* setup OpenGL state */
  840.     glEnable(GL_DEPTH_TEST);
  841.     glDepthFunc(GL_LEQUAL);
  842.     glClearDepth(1.0);
  843.     glMatrixMode(GL_PROJECTION);
  844.     glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 80);
  845.     glMatrixMode(GL_MODELVIEW);
  846.  
  847.     MakeCube();
  848.  
  849.     if (argv[1] != NULL) {
  850.     numMessages = argc - 1;
  851.     messages = &argv[1];
  852.     } else {
  853.     numMessages = NUM_DEFAULT_MESSAGES;
  854.     messages = defaultMessage;
  855.     }
  856.  
  857.     base = glGenLists(numMessages + 1);
  858.     SetupMessageDisplayList(&fontEntry[0], numMessages, messages);
  859.  
  860.     tick();
  861.  
  862.     /* start event processing */
  863.     XtAppMainLoop(app);
  864. }
  865.